﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;

namespace MvcExtensions.Core
{
    public class ObjectAccessor
    {
        //TODO : move this code into some common lib or class
        private static readonly ConcurrentDictionary<Type, IEnumerable<PropertyInfo>> PropertyInfos = new ConcurrentDictionary<Type, IEnumerable<PropertyInfo>>();

        public static IEnumerable<PropertyInfo> GetPropertyInfos(object model)
        {
            var modelType = model.GetType();
            return GetPropertyInfos(modelType);
        }

        public static IEnumerable<PropertyInfo> GetPropertyInfos(Type type)
        {
            return PropertyInfos.GetOrAdd(type, t => type.GetProperties());
        }

        public static object GetMetadataValue(string metadata, object model)
        {
            if (model == null || string.IsNullOrWhiteSpace(metadata))
                return null;

            var modelType = model.GetType();
            var modelPropertyInfos = PropertyInfos.GetOrAdd(modelType, t => modelType.GetProperties());

            var propertyName = metadata;
            var dotLocation = metadata.IndexOf('.');
            var arrayPosition = 0;
            if (dotLocation > 0)
            {
                propertyName = metadata.Substring(0, dotLocation);
            }

            var isArray = propertyName.Contains('[') && propertyName.Contains(']');
            if (isArray)
            {
                var braketLocStart = propertyName.IndexOf('[');
                var braketLocEnd = propertyName.IndexOf(']');
                var arrayPos = propertyName.Substring(braketLocStart + 1, (braketLocEnd - braketLocStart) - 1);

                propertyName = metadata.Substring(0, braketLocStart);

                if (!int.TryParse(arrayPos, out arrayPosition))
                {
                    return null;
                }
            }

            var propertyInfo = modelPropertyInfos.FirstOrDefault(x => x.Name == propertyName);

            if (propertyInfo != null)
            {
                var value = propertyInfo.GetValue(model, null);
                if (isArray && arrayPosition >= 0)
                {
                    var list = (value as IEnumerable<object>);
                    if (list == null)
                        return null;
                    //pass the array object
                    value = list.ToList()[arrayPosition];
                }
                if (dotLocation > 0)
                {
                    var childPropertyName = metadata.Substring(dotLocation + 1);
                    return GetMetadataValue(childPropertyName, value);
                }
                return value;
            }
            return null;
        }
    }



}
